<?php

include_once 'db.connection.class.php';

class Model{
	
	public $status;
	
	public $debug = false;
	
	protected $model;
	protected $definitions;
	
	protected $fields;
	private $_connection;
	private $_connectionParameters;
	
	private $encryption_key = 'sr2mc1c4';

	private $encoding = "utf8";
	
	public $order;
	public $order_direction;
	
	public $table_index = 0;
	
	function __construct(){
		$this->initModel();
	}
	
	function initModel(){
		
		// init DB connection
		$status = array(
			"code" => 0,
			"text" => ""
		);
		$this->_connectionParameters = ConnectionParameters::get();
		$this->_connection = mysql_connect($this->_connectionParameters->host.":".$this->_connectionParameters->port, $this->_connectionParameters->username, $this->_connectionParameters->password);
		if (!$this->_connection){
			$status["code"] = mysql_errno();
			$status["text"] = mysql_error();
		}else if (!mysql_select_db($this->_connectionParameters->dbname,$this->_connection)){
			$status["code"] = mysql_errno();
			$status["text"] = mysql_error();
		}
		$this->_alter("SET NAMES $this->encoding");
		$this->status = $status;
		
		// init DB model
			// init tables
			if (isset($this->definitions["tables"]) && count($this->definitions["tables"])>0){
				$defs = array();
				foreach ($this->definitions["tables"] as $name){
					$fields = $this->_query("DESCRIBE $name");
					if ($fields){
						$tmp = array();
						foreach ($fields as $k=>$field){
							$key = $field["Field"];
							$tmp[$key]["name"] = $key;
							
							$tmp[$key]["type"] = preg_replace('/\(\d\)\s*.*?/',"",$field["Type"]);
							$tmp[$key]["null"] = preg_match('/yes/i',$field["Null"])?true:false;
							$tmp[$key]["key"] = $field["Key"];
							$tmp[$key]["default"] = $field["Default"];
						}
						$fields = $tmp;
					}
					$defs[$name]["fields"] = $fields;
				}
				$this->definitions["tables"] = $defs;
			}
			// init joins
			if (isset($this->definitions["joins"]) && count($this->definitions["joins"])>0){
				$defs = array();
				foreach ($this->definitions["joins"] as $src=>$dest){
					$tmp = explode(".",$src);
					$table1 = $tmp[0];
					$attr1 = $tmp[1];
					
					$tmp = explode(".",$dest);
					$table2 = $tmp[0];
					$attr2 = $tmp[1];
					
					if(isset($this->definitions["tables"][$table1]["fields"][$attr1])){
						$defs["$table1|$table2"] = "$attr1|$attr2";
					}
				}
				$this->definitions["joins"] = $defs;
			}
	}
	
	function _query($q,$assoc=true){
		$res = mysql_query($q,$this->_connection);
		if ($res){
			$ret = array();
			while ($r = $assoc?mysql_fetch_assoc($res):mysql_fetch_row($res)){
				$ret[] = $r;
			}
		}
		return $ret;
	}
	function _alter($q) {
		$res = mysql_query($q,$this->_connection);
		return mysql_affected_rows();
	}
	
	function count($field="*",$filter=null) {
		$ret = $this->findOne("COUNT($field)",$filter,false);
		return (int)$ret;
	}
	
	function find( $fields="*", $filter=null, $limit=array(0,10) ) {
		
		if (!$fields) {
			return;
		}
		
		if($fields && !is_array($fields)) {
			$fields = array($fields);
		}
		
		$join = isset($this->definitions["joins"]);
		
		$q = "SELECT ";
		if(count($fields)==1 && $fields[0]=="*") {
			if($join) {
				foreach ($this->definitions["tables"] as $table_name=>$table_data) {
					foreach ($table_data["fields"] as $table_field_name=>$defs) {
						$q .= "`$table_name`.`$table_field_name`";
						if (isset($this->definitions["joins"])) {
							$q .= "as `$table_name#$table_field_name`";
						}
						$q .= ", ";
					}
				}
				$q = substr($q,0,-2)." ";
			}else{
				$q .= "* ";
			}
		}else{
			$q .= implode(",",$fields)." ";
		}
		$q .= "FROM ";
		if ($join) {
			foreach ($this->definitions["joins"] as $tables=>$attrs) {
				list($table1,$table2) = explode("|",$tables,2);
				list($attr1,$attr2) = explode("|",$attrs,2);
				$q .= "`$table1` ".$this->definitions["join_type"]." JOIN `$table2` ON `$table1`.`$attr1`= `$table2`.`$attr2` ";
			}
		}else{
			$tbl_names = array_keys($this->definitions["tables"]);
			$tbl = $tbl_names[$this->table_index];
			$q .= "`$tbl`";
		}
		$q .= " WHERE 1";
			$q .= $this->_parseFilter($filter);
		
		if ($this->order) {
			$this->order = preg_replace('/^`|`$/',"",$this->order);
			$order_dir = preg_match('/^ASC|DESC$/i',$this->order_direction) ? $this->order_direction : "ASC";
			$q .= " ORDER BY `$this->order` $order_dir";
		}
		if ($limit !== false) {
			$q .= " LIMIT ".$limit[0].", ".$limit[1];
		}
		if ($this->debug){
			echo "\n\n<p>\n$q\n</p>\n\n";
		}
		
		return $this->_query($q);
	}
	
	function findRow($fields="",$filter=null) {
		$ret = $this->find($fields,$filter);
		if (is_array($ret)) {
			$ret = $ret[0];
		}else{
			$ret = null;
		}
		
		return $ret;
	}
	
	function findOne($fields="",$filter=null) {
		$ret = $this->findRow($fields,$filter);
		if (is_array($ret)) {
			$keys = array_keys($ret);
			$ret = $ret[$keys[0]];
		}else{
			$ret = null;
		}
		
		return $ret;
	}

	function update($fields,$filters,$index=0) {
		
		$tables = array_keys($this->definitions["tables"]);
		$table = $tables[$index];
			
		$q = "UPDATE $table "
			."SET ";
			foreach ($fields as $field_name=>$field_value) {
				$q .= "$field_name='".str_replace("'","\\'",$field_value)."', ";
			}
			$q = substr($q,0,strlen($q)-2);
			$q .= " WHERE 1";
			foreach ($filters as $field_name=>$field_value) {
				$q .= " AND $field_name='".str_replace("'","\\'",$field_value)."'";
			}
			
		if ($this->debug){
			echo "\n\n<p>\n$q\n</p>\n\n";
		}

		$this->_alter($q);
		return $this->_query_return_status();
	}
	
	function insert($fields,$index=0) {
		
		$tables = array_keys($this->definitions["tables"]);
		$table = $tables[$index];
		
		$q = "INSERT INTO $table(".implode(",",array_keys($fields)).") "
			."VALUES (";			
			foreach ($fields as $field_name=>$field_value) {
				
				$type = $this->definitions["tables"][$table]["fields"][$field_name]["type"];
				if (preg_match('/^int|^float/i',$type)) {
					$q .= ($field_value>0 ? $field_value : "NULL").", ";
				}else{
					$q .= "'".str_replace("'","\\'",$field_value)."', ";
				}
			}
			$q = substr($q,0,strlen($q)-2).");";

		$ret = $this->_alter($q);
		$id = $this->_query("SELECT LAST_INSERT_ID() as id FROM $table LIMIT 1");
		if($id) {
			$id = $id[0]["id"];
		}
		
		$error = mysql_errno($this->_connection);
		if ($this->debug){
			echo "\n\n<p>\n";
			echo "$q<br />returned: ".mysql_error();
			echo "\n</p>\n\n";
		}
		
		$ret = $error ? false : $id;
		
		return $ret;
		
	}

	function delete($filter,$index=0) {
		$tables = array_keys($this->definitions["tables"]);
		$table = $tables[$index];
		
		$q = "DELETE FROM $table WHERE 1".$this->_parseFilter($filter);
			
		if ($this->debug){
			echo "\n\n<p>\n$q\n</p>\n\n";
		}
		
		$ret = $this->_alter($q);
		
		$error = mysql_errno($this->_connection);
		$ret = $error ? false : true;
		
		return $ret;
	}
	
	//----------------------------------------
	
	private function _parseFilter($filter) {
		$ret = "";
		if (is_array($filter)) {
			foreach ($filter as $k=>$v) {
				$test = $k;
				if (preg_match('/^[\s\r\n\t]*[<>=!]/',$v)) {
					$test = "custom_compare";
				}
				switch ($test) {
					case "password":
						$compare = "=";
						$v = "PASSWORD('$v')";
						break;
					case "custom_compare":
						$compare = "";
						break;
					default:
						$compare = "LIKE";
						$v = "'$v'";
				}
				$ret .= " AND ($k $compare $v)";
			}
		}
		return $ret;
	}
	
	public function getErrorMessage() {
		if($this->error["code"]) {
			return $this->error["code"].". ".$this->error["message"];
		}else {
			return "";
		}
	}
	
	private function _query_return_status() {
		$error = mysql_errno($this->_connection);
		if($error) {
			$this->error = array(
				"code" => $error,
				"message" => mysql_error($this->_connection)
			);
			return false;
		}else{
			$this->error = array(
				"code" => 0,
				"message" => ""
			);
			return true;
		}
	}
}

?>